package net.zoneland.knowledge.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.base.Preconditions;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import lombok.SneakyThrows;
import net.zoneland.knowledge.config.MeetingConfig;
import net.zoneland.knowledge.dao.MeetingQrMapper;
import net.zoneland.knowledge.dao.MeetingSignInMapper;
import net.zoneland.knowledge.dao.PartyUserMapper;
import net.zoneland.knowledge.enums.CmpDnEnum;
import net.zoneland.knowledge.model.LoginUserPojo;
import net.zoneland.knowledge.model.MeetingQr;
import net.zoneland.knowledge.model.MeetingSignIn;
import net.zoneland.knowledge.model.PartyUser;
import net.zoneland.knowledge.service.MeetingQrService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 会议业务接口实现.
 * @author chw.
 * @version V1.0
 * 2022-11-18
 */
@Service
public class MeetingQrServiceImpl implements MeetingQrService {

    /**
     * copy对象属性忽略的字段
     */
    private static final String[] IGNORE_PROPERTIES_WHEN_COPY = new String[] { "id", "creatorId",
            "creatorName", "departmentDn", "companyName", "crateTime", "lastModified", "meetingSignIn"};

    /**
     * 会议(二维码)数据接口.
     */
    @Autowired
    private MeetingQrMapper meetingQrMapper;

    /**
     * 会议签到数据接口.
     */
    @Autowired
    private MeetingSignInMapper meetingSignInMapper;

    /**
     * 党员数据接口.
     */
    @Autowired
    private PartyUserMapper partyUserMapper;


    /**
     * api后台地址.
     */
    @Autowired
    private MeetingConfig meetingConfig;

    /**
     * 根据会议id获取会议信息.
     * @param id 会议id
     * @return 会议信息
     */
    @Override
    public MeetingQr search(final String id) {
        return meetingQrMapper.search(id);
    }

    /**
     * 根据会议id修改会议状态
     * @param meetingQr 会议二维码实体类
     * @return 删除条数
     */
    @Override
    public int editMeetingQrStatus(final MeetingQr meetingQr) {
       return meetingQrMapper.editMeetingQrStatus(meetingQr.getId(), meetingQr.isStatus());
    }

    /**
     * 根据创建人id查询创建的会议
     * @param creatorId 创建人id
     * @return 创建会议集合
     */
    @Override
    public List<MeetingQr> queryMeetingByCreateId(final String creatorId) {
        List<MeetingQr> meetingQrs = meetingQrMapper.queryMeetingByCreateId(creatorId);
        return meetingQrs;
    }

    /**
     * 查询参会二维码
     * @param id 主键id
     * @return
     */
    @Override
    public MeetingQr queryMeetingQrCode(final String id) {
        return meetingQrMapper.queryMeetingQrCode(id);
    }

    /**
     * 会议详情
     * @param id 会议(二维码).
     * @return
     */
    @Override
    public MeetingQr queryMeetingContent(final String id) {
        return meetingQrMapper.queryMeetingContent(id);
    }

    /**
     * 保存 编辑会议信息
     * @param meetingQr 会议信息
     * @param user 登录用户信息
     */
    @SneakyThrows
    @Override
    public int saveMeeting(final MeetingQr meetingQr, final LoginUserPojo user) {
            Preconditions.checkNotNull(user, "登录人员信息未获取!");
            final String oldId = meetingQr.getId();
            if(StringUtils.isNotBlank(oldId)) { // 更新
                //获取议会信息
                final MeetingQr meeting = search(oldId);
                BeanUtils.copyProperties(meetingQr, meeting, IGNORE_PROPERTIES_WHEN_COPY);
                meeting.setLastModified(LocalDateTime.now());
                return meetingQrMapper.saveOrUpdate(meeting);
            } else { // 新增
                meetingQr.setId(UUID.randomUUID().toString());
                final byte[] meetingCode = createMeetingQr(makeLink(meetingQr.getId()));
                meetingQr.setCreatorId(user.getUserID());
                meetingQr.setCreatorName(user.getUserName());
                meetingQr.setCompanyName(user.getCompanyName());
                meetingQr.setDepartmentDn(user.getDepartmentDN());
                meetingQr.setCreateTime(LocalDateTime.now());
                meetingQr.setMeetingQrCode(meetingCode);
                return meetingQrMapper.save(meetingQr);
            }
    }

    /**
     * 生成会议二维码.
     * @param link 签到链接，含会议id
     * @return 二维码二级制信息
     * @throws WriterException 读写异常
     * @throws IOException io异常
     */
    public byte[] createMeetingQr(final String link) throws WriterException, IOException {
        Preconditions.checkNotNull(link, "链接不能为空!");
        final Map<EncodeHintType, Object> hints = new EnumMap<>(EncodeHintType.class);
        // 指定编码格式
        hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
        // 指定纠错级别(L--7%,M--15%,Q--25%,H--30%)
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        // 编码内容,编码类型(这里指定为二维码),生成图片宽度,生成图片高度,设置参数
        final BitMatrix bitMatrix = new MultiFormatWriter().encode(link, BarcodeFormat.QR_CODE, 300, 300, hints);
        final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        MatrixToImageWriter.writeToStream(bitMatrix,"PNG", byteArrayOutputStream);
        return byteArrayOutputStream.toByteArray();
    }

    /**
     * 编辑链接.
     * @param id 会议id
     * @return 签到链接
     */
    private String makeLink(final String id) {
        Preconditions.checkNotNull(id, "会议id不能为空!");
        return String.format(meetingConfig.getMeetingSignInUrl(), id);
    }

    /**
     * 保存参会人员
     * @param meetingSignIns 参会人员信息
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveMeetingAttendee(final List<MeetingSignIn> meetingSignIns) {
        //因为这里的每条数据的meetingId都是一样的，所以可以直接获取获取第一条的meetingId
        final String meetingId = meetingSignIns.get(0).getMeetingId();
        final MeetingQr meetingQr = meetingQrMapper.getParamByMeetingId(meetingId);
        Assert.isTrue(Objects.nonNull(meetingQr), "会议信息不存在");

        // 查询会议参会人员信息
        final List<MeetingSignIn> meetingSignInList = meetingSignInMapper.querySignInUserByMeetingId(meetingId);

        // 传入数据的参会人员信息列表
        final Set<String> incomingSignInUserIds = meetingSignIns.stream()
                .map(MeetingSignIn::getSignInUserId)
                .collect(Collectors.toSet());

        // 原来的参会人员信息列表
        final Set<String> originalSignInUserIds = meetingSignInList.stream()
                .map(MeetingSignIn::getSignInUserId)
                .collect(Collectors.toSet());

        // 需要删除的用户数据ids
        final List<String> deleteSignInUserId = originalSignInUserIds.stream()
                .filter(signInUserId -> !incomingSignInUserIds.contains(signInUserId))
                .collect(Collectors.toList());

        // 根据ids删除参会人员信息
        if (CollectionUtils.isNotEmpty(deleteSignInUserId)) {
            meetingSignInMapper.deleteByIds(deleteSignInUserId, meetingId);
        }

        // 需要新增的用户数据
        final List<MeetingSignIn> addMeetingSignInList = meetingSignIns.stream()
                .filter(meetingSignIn -> !originalSignInUserIds.contains(meetingSignIn.getSignInUserId()))
                .collect(Collectors.toList());

        final LocalDateTime now = LocalDateTime.now();
        // 新增参会用户信息
        addMeetingSignInList.forEach(meetingSignIn -> {
            meetingSignIn.setMeetingTitle(meetingQr.getMeetingTitle());
            meetingSignIn.setId(UUID.randomUUID().toString());
            meetingSignIn.setCrateTime(now);
            meetingSignIn.setLastModified(now);
            final PartyUser partyUserByOaId = partyUserMapper.queryByUserId(meetingSignIn.getSignInUserId());
            if (null != partyUserByOaId) {
                meetingSignIn.setMobile(partyUserByOaId.getMobile());
                meetingSignIn.setMeetingId(meetingQr.getId());
                meetingSignIn.setEmpNo(partyUserByOaId.getYgId());
                meetingSignIn.setJob(partyUserByOaId.getAdministrativeDuties());
                meetingSignIn.setCompanyDn(partyUserByOaId.getCompanyDn());
                meetingSignIn.setCompanyName(CmpDnEnum.getNameByValue(partyUserByOaId.getCompanyDn()));
            }
            meetingSignInMapper.insertMeetingSignInUserData(meetingSignIn);
        });
        //根据meeting更新会议人员数量
        meetingQrMapper.updateMeetingById(meetingQr.getId());

        // 根据会议id更新参会人员数量
        meetingQrMapper.updateMeetingSignCount(meetingQr.getId());
    }

    @Override
    public void deleteSignInfo(final String id) {
        meetingSignInMapper.deleteById(id);
    }

    @Override
    public void deleteImportCountNum(final String meetingId) {
        // 删除人员时，同步更新参会人员数量
        meetingQrMapper.updateMeetingImportCount(meetingId);

        // 删除人员时，同步更新参加的会议的签到数量
        meetingQrMapper.updateMeetingSignCount(meetingId);
    }

    /**
     * 会议分页数据.
     * @param currPage 当前页
     * @param pageSize 每页条数
     * @return
     */
    @Override
    public PageInfo<MeetingQr> queryPager(final Integer currPage, final Integer pageSize, final MeetingQr meetingQr) {
        PageHelper.startPage(currPage, pageSize);
        final List<MeetingQr> meetingList = meetingQrMapper.queryPager(meetingQr);
        // 使用 PageInfo 对象来获取总条数和总页数
        final PageInfo<MeetingQr> pageInfo = new PageInfo<>(meetingList);
        return pageInfo;
    }
}
